package nkr1pt.popkorn.extract;

import com.google.inject.Inject;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import nkr1pt.popkorn.download.DownloadCandidate;
import nkr1pt.popkorn.download.DownloadCandidateItem;
import nkr1pt.popkorn.download.DownloadCandidateState;
import nkr1pt.popkorn.events.EventBus;
import nkr1pt.popkorn.events.PopkornEvent.EventType;
import nkr1pt.popkorn.events.PopkornEventFactory;

/**
 * Extracts archives after they are downloaded.
 * Since no native Java unrar library is available, extracting of rar archives is delegated to a
 * capable tool on the host operating system, if such a tool is present.
 *
 * @author Kristof Vanhaeren
 */
public class Extractor {

    private EventBus eventBus;
    private PopkornEventFactory eventFactory;
    private ExtractCommandsMap commandMap;
    private static final Logger logger = Logger.getLogger(Extractor.class.getName());

    @Inject
    public Extractor(EventBus eventBus, PopkornEventFactory eventFactory, ExtractCommandsMap commandMap) {
        this.eventBus = eventBus;
        this.eventFactory = eventFactory;
        this.commandMap = commandMap;
    }

    public boolean extract(DownloadCandidate downloadCandidate) {
        for (ExtractCommand command : getSystemSpecificExtractCommands()) {
            try {
                logger.log(Level.INFO, "Checking RAR archives from {0} for CRC errors using {1}", new Object[]{downloadCandidate.getName(), command.getName()});
                Set<DownloadCandidateItem> crcErrors = command.testCRC(downloadCandidate);
                if (!crcErrors.isEmpty()) {
                    logger.log(Level.INFO, "CRC errors were found for {0}, corrupt archives will be downloaded again.", new Object[]{downloadCandidate.getName()});
                    downloadCandidate.addCorruptedItems(crcErrors);
                } else {
                    downloadCandidate.setState(DownloadCandidateState.EXTRACTING);
                    logger.log(Level.INFO, "Attempting to extract {0} RAR archives using {1}", new Object[]{downloadCandidate.getName(), command.getName()});
                    if (command.extract(downloadCandidate)) {
                        logger.log(Level.INFO, "Extraction of {0} succeeded.", downloadCandidate.getName());
                        downloadCandidate.setState(DownloadCandidateState.EXTRACTED);
                        eventBus.fireEvent(eventFactory.newEvent(EventType.EXTRACTION_COMPLETED, downloadCandidate));
                        return true;
                    }
                }
            } catch (Exception e) {
                logger.log(Level.SEVERE, "Extraction of {0} failed.\n{1}", new Object[]{downloadCandidate.getName(), e.getMessage()});
            }
        }

        eventBus.fireEvent(eventFactory.newEvent(EventType.EXTRACTION_FAILED, downloadCandidate));
        return false;
    }

    private ExtractCommand[] getSystemSpecificExtractCommands() {
        return commandMap.getExtractCommands().get(getOsName());
    }

    protected String getOsName() {
        String os = System.getProperty("os.name");

        if (Pattern.compile("linux", Pattern.CASE_INSENSITIVE).matcher(os).find()) {
            return "linux";
        } else if (Pattern.compile("windows", Pattern.CASE_INSENSITIVE).matcher(os).find()) {
            return "windows";
        } else if (Pattern.compile("mac os x", Pattern.CASE_INSENSITIVE).matcher(os).find()) {
            return "mac osx";
        }

        return null;
    }
}
